//六 移位运算(了解)
//移位运算符有三个: << >> >>> ，都是⼆元运算符，且都是按照⼆进制⽐特位来运算的。


public class J20250228 {
    public static void main1(String[] args) {
        //1. 左移<< 二进制最左侧位不要了,最右侧补0 相当于将该数乘以 2 的 n 次方。
        int a = 0x10;
        System.out.printf("%x\n", a << 1);//20 a为进行移位要操作的数 1为移位的个数 (注意, 运⾏结果是按⼗六进制打印的)
        //注意：向左移位时，丢弃的是符号位，因此正数左移可能会变成负数。

        //2. 右移>> 二进制最右侧位不要了,最左侧补符号位(正数补0,负数补1) 相当于将该数除以 2 的 n 次方并向下取整（即省略小数点后的数）
        int b = 0x10;
        System.out.printf("%x\n", b >> 1);//8 运⾏结果(注意, 是按⼗六进制打印的)
        int c = 0xffff0000;
        System.out.printf("%x\n", c >> 1);//ffff8000 运⾏结果(注意, 是按⼗六进制打印的)

        //3. ⽆符号右移>>> 最右侧位不要了,最左侧补0.
        int d = 0xffffffff;
        System.out.printf("%x\n", d >>> 1);//7fffffff 运⾏结果(注意, 是按⼗六进制打印的)
        //注意:
        //1. 左移1位,相当于原数字*2.左移N位,相当于原数字*2的N次⽅.【针对正数⽽⾔】
        //2. 右移1位,相当于原数字/2.右移N位,相当于原数字/2的N次⽅.【针对正数⽽⾔】
        //3. 由于计算机计算移位效率⾼于计算乘除,当某个代码正好乘除2的N次⽅的时候可以⽤移位运算代替.【针对正数⽽⾔】
        //4. 移动负数位或者移位位数过⼤都没有意义.
        //思考：如果是负数进⾏左移和右移，怎么计算结果？
    }

    //1. 负数左移运算符 <<
    //运算规则：负数在 Java 中以补码形式存储，左移操作是将补码向左移动指定的位数，右边空出的位用 0 填充。左移 n 位相当于将该数乘以 2 的 n 次方。
    //示例代码：
    public static void main2(String[] args) {
        int a = -5;
        // -5 的原码: 1000 0000 0000 0000 0000 0000 0000 0101
        // -5 的反码: 1111 1111 1111 1111 1111 1111 1111 1010
        // -5 的补码: 1111 1111 1111 1111 1111 1111 1111 1011
        int b = a << 2;
        System.out.println("原始值: " + a);//原始值: -5   (-5的补码为 1111 1111 1111 1111 1111 1111 1111 1011)
        System.out.println("左移 2 位后的值: " + b);//左移 2 位后的值: -20  (左移 2 位后补码变为 1111 1111 1111 1111 1111 1111 1110 1100，转换为十进制就是-20)

        //2. 负数有符号右移运算符 >>
        //运算规则：将负数的补码向右移动指定的位数，左边空出的位用符号位（即 1）填充。右移 n 位相当于将该数除以 2 的 n 次方并向下取整。
        //示例代码：
        int c = -20;
        // -20 的原码: 1000 0000 0000 0000 0000 0000 0001 0100
        // -20 的反码: 1111 1111 1111 1111 1111 1111 1110 1011
        // -20 的补码: 1111 1111 1111 1111 1111 1111 1110 1100
        int d = c >> 2;
        System.out.println("原始值: " + c);//原始值: -20  (-20 的补码为 1111 1111 1111 1111 1111 1111 1110 1100)
        System.out.println("有符号右移 2 位后的值: " + d);//有符号右移 2 位后的值: -5  (有符号右移 2 位后补码变为 1111 1111 1111 1111 1111 1111 1111 1011，转换为十进制就是 -5）

        //3. 负数无符号右移运算符 >>>
        //运算规则：将负数的补码向右移动指定的位数，左边空出的位用 0 填充。由于左边用 0 填充，无符号右移后结果通常为正数。
        //示例代码
        int e = -20;
        // -20 的原码: 1000 0000 0000 0000 0000 0000 0001 0100
        // -20 的反码: 1111 1111 1111 1111 1111 1111 1110 1011
        // -20 的补码: 1111 1111 1111 1111 1111 1111 1110 1100
        int f = e >>> 2;
        System.out.println("原始值: " + e);//原始值: -20
        System.out.println("无符号右移 2 位后的值: " + f);//无符号右移 2 位后的值: 1073741819
        //总结来说，负数在这三种位运算下的主要区别在于移位后空位的填充方式：左移统一用 0 填充右边空位；有符号右移用符号位 1 填充左边空位；无符号右移用 0 填充左边空位。
    }


//七. 条件运算符
    //条件运算符只有⼀个:
    //Java 条件运算符里只有布尔表达式的结果必须是布尔类型，而表达式 1 和表达式 2 可以是任意类型，只要类型兼容就行
    //表达式1 ? 表达式2 : 表达式3
    //当表达式1 的值为true时,整个表达式的值为表达式2 的值;
    //当表达式1 的值为false时,整个表达式的值为表达式3 的值.
    //也是Java中唯⼀的⼀个三⽬运算符,是条件判断语句的简化写法

    public static void main3(String[] args) {
        // 求两个整数的最⼤值
        int a = 10;
        int b = 20;
        int max = a > b ? a : b;
        System.out.println(max);//20

        //注意：1. 表达式2和表达式3的结果要是同类型的，除⾮能发⽣类型隐式类型转换
        //int c = 10;
        //int d = 20;
        //int sum = c > d? 1 : 2.0;  //发生错误 不兼容的类型: 从double转换到int可能会有损失

        //2. 表达式不能单独存在，其产⽣的结果必须要被使⽤
        //int e = 10;
        //int f = 20;
        //e > f? e : f;  //报错：Error:(15, 14) java: 不是语句
    }

    //八. 运算符的优先级
    //在⼀条表达式中，各个运算符可以混合起来进⾏运算，但是运算符的优先级不同，⽐如：*和/的优先
    //级要⾼于+和-，有些情况下稍不注意，可能就会造成很⼤的⿇烦。
    public static void main4(String[] args) {
        // 求a和b的平均值
        int a = 10;
        int b = 20;
        int c = a + (b - a) >> 1;
        System.out.println(c);
        //上述表达式中，由于+的优先级要⾼于>>,因此a先和b-a的结果做加法，整体为20，最后再进⾏右移，因此结果为10。
        //注意：运算符之间是有优先级的.具体的规则我们不必记忆.在可能存在歧义的代码中加上括号即可
    }
}